package com.wen.es.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.wen.es.annotation.EsTypeAnno;
import com.wen.es.domain.ChildType;
import com.wen.es.domain.ParentType;
import com.wen.es.service.EsService;
import com.wen.es.service.entity.EsDataWrap;
import com.wen.es.utils.EsConstants;
import com.wen.es.utils.JsonTools;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.admin.indices.exists.types.TypesExistsResponse;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Created by LAIO on 2018/6/30.
 */
public class EsServiceImpl implements EsService {


    private static Client clientInstance = null;
    private static final Object OBJ_LOCK = new Object();

    static {
        try {
            clientInstance = getClientInstance();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

    private Client client = getClientInstance();

    public EsServiceImpl() throws UnknownHostException {
    }


    /**
     * 创建连接
     *
     * @return 返回客户端
     * @throws UnknownHostException 未知主机
     */
    private static Client getClientInstance() throws UnknownHostException {
        if (clientInstance == null) {
            synchronized (OBJ_LOCK) {
                if (clientInstance != null) {
                    return clientInstance;
                }
                Settings settings = Settings.settingsBuilder()
                        //*设置集群名称*/
                        .put("cluster.name", "elasticsearch")
                        //是否自动添加集群中的节点
                        .put("client.transport.sniff", true).build();
                clientInstance = TransportClient.builder().settings(settings).build()
                        .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300));
            }
        }
        return clientInstance;
    }

    /**
     * 删除索引
     *
     * @param indexName 索引名
     * @throws IOException 删除异常
     */
    @Override
    public void deleteIndex(String indexName) throws IOException {
        IndicesExistsResponse indexReponse = client.admin().indices().prepareExists(indexName).execute().actionGet();
        if (indexReponse.isExists()) {
            client.admin().indices().prepareDelete(indexName).execute().actionGet();
        }

    }

    /**
     * 创建索引
     * indexName 索引名称
     *
     * @throws IOException 创建异常
     */
    @Override
    public void craeteIndex(String indexName) throws IOException {
        EsTypeAnno parent = ParentType.class.getAnnotation(EsTypeAnno.class);
        EsTypeAnno child = ChildType.class.getAnnotation(EsTypeAnno.class);
        XContentBuilder parentBuild = null;
        XContentBuilder childBuild = null;
        IndicesExistsResponse indexReponse = client.admin().indices().prepareExists(indexName).execute().actionGet();
        if (indexReponse.isExists()) {
            TypesExistsResponse typeReponse;
            typeReponse = client.admin().indices().prepareTypesExists(indexName).setTypes(parent.propertyName()).execute().actionGet();
            if (!typeReponse.isExists()) {
                parentBuild = XContentFactory.jsonBuilder();
            }
            typeReponse = client.admin().indices().prepareTypesExists(indexName).setTypes(child.propertyName()).execute().actionGet();
            if (!typeReponse.isExists()) {
                childBuild = XContentFactory.jsonBuilder();
            }
        } else {
            parentBuild = XContentFactory.jsonBuilder();
            childBuild = XContentFactory.jsonBuilder();
        }
        if (parentBuild == null && childBuild == null) {
            System.err.println(indexName + " is exists");
            return;
        }
        CreateIndexRequestBuilder indices = client.admin().indices()
                .prepareCreate(indexName);
        if (parentBuild != null) {
            generateMappings(parentBuild, ParentType.class);
            indices.addMapping(parent.propertyName(), parentBuild);
            System.out.println(parentBuild.string());
            System.out.println(JSON.toJSONString(JSON.parseObject(parentBuild.string()), SerializerFeature.PrettyFormat));
        }
        if (childBuild != null) {
            generateMappings(childBuild, ChildType.class);
            indices.addMapping(child.propertyName(), childBuild);
            System.out.println(childBuild.string());
            System.out.println(JSON.toJSONString(JSON.parseObject(childBuild.string()), SerializerFeature.PrettyFormat));
        }

        indices.execute().actionGet();
    }

    /**
     * 上传数据
     *
     * @param index        索引
     * @param type         类型
     * @param dataWrapList 数据包装
     */
    @Override
    public void uploadData(String index, String type, List<EsDataWrap> dataWrapList) {
        if (dataWrapList == null || dataWrapList.isEmpty()) {
            return;
        }
        BulkRequestBuilder bulkRequest = client.prepareBulk();
        IndexRequestBuilder bulkData;
        String json;
        for (EsDataWrap item : dataWrapList) {
            json = JSON.toJSONString(JsonTools.parseMap(item.getItem()));
            bulkData = client.prepareIndex(index, type, item.getId())
                    .setSource(json);
            if (item.getParentId() != null) {
                bulkData.setParent(item.getParentId());
            }
            bulkRequest.add(bulkData);
//            System.out.println(json);
        }
        BulkResponse bulkResponse = bulkRequest.get();

        if (bulkResponse.hasFailures()) {
            // process failures by iterating through each bulk response item
            throw new RuntimeException("Upload error!");
        }
    }

    private void generateMappings(XContentBuilder builder, Class<?>... clzs) throws IOException {
        if (clzs == null) {
            return;
        }
        builder.startObject();
        for (Class clz : clzs) {
            generateType(builder, clz);
        }
        builder.endObject();
    }

    private void generateType(XContentBuilder builder, Class<?> clz) throws IOException {
        EsTypeAnno anno = clz.getAnnotation(EsTypeAnno.class);
        if (Objects.equals(anno.type(), EsConstants.TYPE_CHILD)) {
            EsTypeAnno parentAnno = anno.refer().getAnnotation(EsTypeAnno.class);
            builder.startObject(EsConstants.KEY__PARENT);
            builder.field(EsConstants.KEY_TYPE, parentAnno.propertyName());
            builder.endObject();
        }
        builder.startObject(EsConstants.KEY_PROPERTIES);
        generateField(builder, clz);
        builder.endObject();
    }

    private void generateField(XContentBuilder builder, Class<?> clz) throws IOException {
        Field[] fields = clz.getDeclaredFields();
        EsTypeAnno anno;
        for (Field f : fields) {
            anno = f.getAnnotation(EsTypeAnno.class);
            if (Objects.equals(anno.type(), EsConstants.TYPE_NESTED)) {
                builder.startObject(anno.propertyName());
                builder.field(EsConstants.KEY_TYPE, EsConstants.TYPE_NESTED);
                builder.startObject(EsConstants.KEY_PROPERTIES);
                generateField(builder, anno.refer());
                builder.endObject();
                builder.endObject();
            } else {
                builder.startObject(anno.propertyName());
                builder.field(EsConstants.KEY_INDEX, anno.index());
                builder.field(EsConstants.KEY_TYPE, anno.type());
                builder.endObject();
            }
        }
    }
}
